<?php

/*
 * 对外API接口
 */

/**
 * Description of IndexController
 *
 * @author yunbo
 */

namespace Api\Controller;

class IndexController extends BaseController {

    private $_method = ''; //当前请求的方法
    private $_data = ''; //客户端传递过来的数据
    private $_default_method = 'get'; //默认请求方式
    private $_agin_field = 'sgin'; //加密字段
    private $_check_fields = array('app_id' => '商户号', 'method' => '请求方法', 'sgin' => '验证SGIN'); //必须存在的字段
    private $_allow_method = array('get', 'post'); //允许请求的方法
    private $allow_output_type = array('json' => 'application/json', 'html' => 'text/html'); //允许输出的资源类型列表

//    
//    function __construct() {
//        parent::__construct();
//        $this->_initialize();
//    }
    //初始化

    public function _initialize() {
        parent::_initialize();
        C('DEFAULT_V_LAYER', 'Api'); //改变模板路径
        // 请求方式检测
        $method = strtolower(REQUEST_METHOD);
        if (!in_array($method, $this->_allow_method)) {
            // 请求方式非法 则用默认请求方法
            $method = $this->_default_method;
        }

        $this->_method = $method;
        if ($this->_method == 'post') {
            $this->_data = I('post.');
        } else {
            $this->_data = I('get.');
        }
        //系统字段进行验证
        $data_key = array_keys($this->_data);
        foreach ($this->_check_fields as $k => $v) {
            if (!in_array($k, $data_key))
                $this->_return("{$v}不能为空", 422);
        }
    }

    /**
     * 对外请求接口
     */
    public function index() {
        //验证商家是否存在
        $customer_data = M('customer')->find($this->_data['app_id']);
        //print_r( $customer_data );
        //商家验证
        if (!$customer_data || !$customer_data['api_role'])
            $this->_return('商家没有权限访问', 400);
        $api_role = unserialize($customer_data['api_role']);
        //接口访问权限验证
        if (!isset($api_role[$this->_data['method']]))
            $this->_return('商家没有权限访问', 400);
        $methods = explode('.', $this->_data['method']);
        $this->_ckechAgin($customer_data['key']);
        //验证插件文件是否存在
        $plug_path = APP_PATH . 'Api/Api/' . $methods[0] . '/';
        if (!file_exists($plug_path . $methods[0] . '.class.php'))
            $this->_return('接口出错文件，请联系平台处理', 400);
        $mode_type = $this->_checkFieldRequestParameter($methods); //请求参数数据字段过滤
        $object_name = 'Api\\Api\\' . $methods[0] . '\\' . $methods[0]; //组合空间命名
        $object = new $object_name();
        $result = $object->$methods[1]($this->_data); //获得返回信息
        switch ($mode_type) {
            case 2 : //返回html
                if ($result === false) {
                    $this->assign('info', array('msg' => $object->getError(), 'url' => $this->_data['return_url']));
                    $this->display($methods[0] . '/Tpl/error');
                } else {
                    //判断浏览器
                    $user_agent = $_SERVER['HTTP_USER_AGENT'];
                    $this->assign('agent_type', 'weixin');
                    //判断是否不是微信浏览器
                    if (strpos($user_agent, 'MicroMessenger') === false) {
                        $this->assign('agent_type', '');
                    }
                    $this->assign('info', $result);
                    $this->display($methods[0] . '/Tpl/Index');
                }
                break;
            case 3 : //返回成功与失败
                if ($result === false) {
                    $this->_return($object->getError(), 400);
                } else {
                    $this->_return("success", 200);
                }
                break;
            default :
                if ($result === false) {
                    $this->_return($object->getError(), 400);
                } else {
                    $this->_returnDataFilter($result, $api_role[implode('.', $methods)], $methods);
                    $this->_return("success", 200, $result);
                }
                break;
        }
    }

    /**
     * 请求参数字段过滤
     * @param array $methods 模块/方法名称2
     */
    private function _checkFieldRequestParameter($methods) {
        if (!$methods)
            return false;
        $data = M('api_module')->alias('am')->join('__API_METHOD__ amd ON amd.module_id = am.id')->where(array('am.module' => $methods[0], 'amd.method' => $methods[1]))->find();
        if (!$data)
            $this->_return('商家未授权', 400);
        $request_parameter = $data['request_parameter'] ? unserialize($data['request_parameter']) : '';
        if ($request_parameter) {
            //去除多余的参数1
            $this->_filterSurplusData($request_parameter);
            //请求参数进行过滤
            foreach ($request_parameter as $k => $v) {
                $flag = false;
                if ($this->_data) {
                    if ($v[2] == 1 && !isset($this->_data[$v[0]]))
                        $this->_return("{$v[1]}参数必传", 400);
                    foreach ($this->_data as $key => $value) {
                        if ($v[0] != $key)
                            continue;
                        $flag = true;
                        //字段类型进行过滤
                        switch ($v[3]) {
                            case 'int' :
                                preg_match('/^[0-9]+$/is', $value) ? '' : $this->_return($v[1] . '数据类型必须为整型', 400);
                                break;
                            case 'float' :
                                preg_match('/^[0-9]+\.[0-9]+$/', $value) ? '' : $this->_return($v[1] . '数据类型必须为浮点型', 400);
                                break;
                            case 'string' :
                                $this->_data[$key] = htmlspecialchars($value);
                                !preg_match('/select|insert|update|delete|\'|\\*|\*|\.\.\/|\.\/|union|into|load_file|outfile/i', $value) ? '' : $this->_return($v[1] . '数据包含攻击字符，请核查', 400);
                                break;
                            case 'date' :
                                preg_match('/^20[0-9]{2}\-[0-9]{2}\-[0-9]{2}\s[0-9]{2}\:[0-9]{2}\:[0-9]{2}$/i', $value) ? '' : $this->_return($v[1] . '日期格式错误', 400);
                                break;
                        }
                    }
                }
                if ($flag === true && $v[2] == 1 && $v[3] == 'json') {
                    $this->_data[$v[0]] = json_decode( html_entity_decode( $this->_data[$v[0]] ), true);
                    //json里面的数据认证
                    foreach ($this->_data[$v[0]] as $k_1 => $v_1) {
                        //判断是否二维数组
                        if (is_array($v_1)) {
                            foreach ($v_1 as $k_3 => $v_3) {
                                $json_flag = false; //不存在字段标识
                                //循环需要过滤的规则
                                foreach ($v[6] as $k_2 => $v_2) {
                                    //验证必穿 但是不存在的字段
                                    if ($v_2[2] == 1 && !isset($this->_data[$v[0]][$k_1][$v_2[0]])) {
                                        $this->_return("{$v_2[1]}参数必传", 400);
                                    } else if ($v_2[0] == $k_3) {//字段匹配
                                        $json_flag = true;
                                        if ($v_2[2] == 1 && !$v_3) { //验证必穿字段
                                            $this->_return((!isset($v_2[5]) ? "{$v_2[1]}参数不存在" : $v_2[5]), 400);
                                        } else if ($v_2[2] == 0 && !$v_3) { //可不用传的字段，但是数据为空给予默认值
                                            $this->_data[$v[0]][$k_1][$v_2[0]] = isset($v_2[4]) ? $v_2[4] : '';
                                        }
                                    }
                                }
                                //如果字段不存在则直接删掉
                                if ($json_flag === false)
                                    unset($this->_data[$v[0]][$k_1][$k_3]);
                            }
                        } else { //一位数组操作
                            $json_flag = false; //不存在字段标识
                            //循环需要过滤的规则
                            foreach ($v[6] as $k_2 => $v_2) {
                                //验证必穿 但是不存在的字段
                                if ($v_2[2] == 1 && !isset($this->_data[$v[0]][$v_2[0]])) {
                                    $this->_return("{$v_2[1]}参数必传", 400);
                                } else if ($v_2[0] == $k_1) {//字段匹配
                                    $json_flag = true;
                                    if ($v_2[2] == 1 && !$v_1) { //验证必穿字段
                                        $this->_return((!isset($v_2[5]) ? "{$v_2[1]}参数不存在" : $v_2[5]), 400);
                                    } else if ($v_2[2] == 0 && !$v_3) { //可不用传的字段，但是数据为空给予默认值
                                        $this->_data[$v[0]][$v_2[0]] = isset($v_2[4]) ? $v_2[4] : '';
                                    }
                                }
                            }
                            //如果字段不存在则直接删掉
                            if ($json_flag === false)
                                unset($this->_data[$v[0]][$k_1]);
                        }
                    }
                } else if ($flag === false && $v[2] == 1) { //验证必须传输的字段
                    $this->_return((!isset($v[5]) ? "{$v[1]}参数不存在！" : $v[5]), 400);
                } else if ($flag === false && $v[2] == 0) { //可不用必须传输的字段
                    $this->_data[$v[0]] = isset($v[4]) ? $v[4] : '';
                }
            }
        }
        return $data['mode_type'];
    }

    /**
     * 请求参数过滤多余的字段
     * @param array $requst_data 过滤规则数据
     */
    private function _filterSurplusData($requst_data) {
        foreach ($this->_data as $key => $value) {
            $flag = false;
            foreach ($requst_data as $k => $v) {
                if ($v[0] == $key || $key == 'app_id') {
                    $flag = true;
                    break;
                }
            }
            if ($flag === false)
                unset($this->_data[$key]);
        }
    }

    /**
     * 返回数据过滤
     * @param array $data 返回的数据
     * @param array $customer_rule 接口返回数据规则
     * @param array $method 访问的方法
     */
    private function _returnDataFilter(&$data, $customer_rule, $methods) {
        if (!$data || !$customer_rule || !$methods)
            return $data;
        $customer_key = $customer_rule == 1 ? 1 : array_keys($customer_rule['fields']);
        $filter_field = array();
        $module_data = M('api_module')->alias('am')->join('__API_METHOD__ amd ON amd.module_id = am.id')->where(array('am.module' => $methods[0], 'amd.method' => $methods[1]))->find();
        $fields = $module_data['fields'] ? unserialize($module_data['fields']) : '';
        if ($fields) {
            if ($customer_key == 1) { //如果是全部字段都返回
                $filter_field = $fields;
            } else {
                //返回设定的字段
                foreach ($fields as $k => $v) {
                    if (in_array($v[0], $customer_key)) {
                        $filter_field[] = $v;
                    }
                }
            }
        }
        //开始过滤
        foreach ($data as $key => $value) {
            foreach ($value as $k => $v) {
                $flag = false;
                foreach ($filter_field as $m => $n) {
                    if ($k == $n[0]) {
                        $flag = true;
                        //动态执行函数处理
                        if (isset($n[3]) && $n[3]) {
                            $data[$key][$k] = $n[3]($v);
                        }
                        //格式化数据类型
                        $data[$key][$k] = $n[2] == 'int' ? (int) $v : ( $n[2] == 'string' ? (string) $v : ( $n[2] == 'float' ? (float) $v : $v ) );
                        break;
                    }
                }
                if ($flag === false)
                    unset($data[$key][$k]); //删除多余字段
            }
        }
    }

    /**
     * agin验证
     * @param string $customer_key 商家KEY
     */
    protected function _ckechAgin($customer_key) {
        $agin = $this->_data[$this->_agin_field]; //获得验证agin
        unset($this->_data[$this->_agin_field]);

        if ($agin != \Api\Server\TrilateralCheckServer::init()->aign($this->_data, $customer_key)) { //验证
            $this->_return('TOKEN效验失败', 413, 0);
        }

        //过滤掉系统字段
        foreach ($this->_check_fields as $k => $v) {
            if ($k != "app_id") {
                unset($this->_data[$k]);
            }
        }
    }

    /**
     * 空页面
     */
    protected function _empty() {
        $this->_return('Method is not allowed to access', 404, 0);
    }

    /**
     * 编码数据
     * @access protected
     * @param mixed $data 要返回的数据
     * @param String $type 返回类型 JSON XML
     */
    protected function _encodeData($data, $type = '') {
        //if(empty($data))  return '';
        if ('json' == $type) {
            // 返回JSON数据格式到客户端 包含状态信息
            $data = json_encode($data);
        } elseif ('xml' == $type) {
            // 返回xml格式数据
            $data = xml_encode($data);
        } elseif ('php' == $type) {
            $data = serialize($data);
        }// 默认直接输出
        $this->_setContentType($type);
        header('Content-Length: ' . strlen($data));
        return $data;
    }

    /**
     * 设置页面输出的CONTENT_TYPE和编码
     * @access public
     * @param string $type content_type 类型对应的扩展名
     * @param string $charset 页面输出编码
     * @return void
     */
    private function _setContentType($type, $charset = '') {
        if (headers_sent())
            return;
        if (empty($charset))
            $charset = C('DEFAULT_CHARSET');
        $type = strtolower($type);
        if (isset($this->allow_output_type[$type])) //过滤content_type
            header('Content-Type: ' . $this->allow_output_type[$type] . '; charset=' . $charset);
    }

    /**
     * 输出返回数据
     * @access protected
     * @param mixed $data 要返回的数据
     * @param String $type 返回类型 JSON XML
     * @param integer $code HTTP状态
     * @return void
     */
    private function _response($data, $type = '', $code = 200) {
        $this->_sendHttpStatus($code);
        exit($this->_encodeData($data, strtolower($type)));
    }

    /**
     * post返回类型
     * @param type $msg
     * @param type $code
     * @param type $data
     * @param type $type
     */
    private function _return($msg = "", $code = 200, $data, $type = 'JSON') {
        foreach ($data as $key => $value) {
            if ($value == null) {
                $data[$key] = "";
            }
        }
        $tips_data = array(
            'msg' => $msg,
            'code' => $code
        );
        if ($data)
            $tips_data['data'] = $data;
        $this->_response($tips_data, $type, $code);
    }

    // 发送Http状态信息
    private function _sendHttpStatus($code) {
        static $_status = array(
            // Informational 1xx
            100 => 'Continue',
            101 => 'Switching Protocols',
            // Success 2xx
            200 => 'OK',
            201 => 'Created',
            202 => 'Accepted',
            203 => 'Non-Authoritative Information',
            204 => 'No Content',
            205 => 'Reset Content',
            206 => 'Partial Content',
            // Redirection 3xx
            300 => 'Multiple Choices',
            301 => 'Moved Permanently',
            302 => 'Moved Temporarily ', // 1.1
            303 => 'See Other',
            304 => 'Not Modified',
            305 => 'Use Proxy',
            // 306 is deprecated but reserved
            307 => 'Temporary Redirect',
            // Client Error 4xx
            400 => 'Bad Request',
            401 => 'Unauthorized',
            402 => 'Payment Required',
            403 => 'Forbidden',
            404 => 'Not Found',
            405 => 'Method Not Allowed',
            406 => 'Not Acceptable',
            407 => 'Proxy Authentication Required',
            408 => 'Request Timeout',
            409 => 'Conflict',
            410 => 'Gone',
            411 => 'Length Required',
            412 => 'Precondition Failed',
            413 => 'Request Entity Too Large',
            414 => 'Request-URI Too Long',
            415 => 'Unsupported Media Type',
            416 => 'Requested Range Not Satisfiable',
            417 => 'Expectation Failed',
            // Server Error 5xx
            500 => 'Internal Server Error',
            501 => 'Not Implemented',
            502 => 'Bad Gateway',
            503 => 'Service Unavailable',
            504 => 'Gateway Timeout',
            505 => 'HTTP Version Not Supported',
            509 => 'Bandwidth Limit Exceeded'
        );
        if (isset($_status[$code])) {
            header('HTTP/1.1 ' . $code . ' ' . $_status[$code]);
            // 确保FastCGI模式下正常
            header('Status:' . $code . ' ' . $_status[$code]);
        }
        header('X-End-Time:' . NOW_TIME);
    }

}
